package sk.naive.talker.adapter;

import sk.naive.talker.*;
import sk.naive.talker.util.*;
import sk.naive.talker.props.*;

import java.rmi.*;
import java.util.*;
import java.util.regex.*;
import java.util.logging.Logger;
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;

/**
 * Skeletal implementation of RemoteUser interface.
 *
 * @author <a href="mailto:virgo@naive.deepblue.sk">Richard "Virgo" Richter</a>
 * @version $Revision: 1.30 $ $Date: 2005/01/11 23:22:36 $
 */
public abstract class AbstractUser extends UnicastRemoteObject implements AdapterUser {
	private Map properties;

	protected RemoteTalker talker;
	protected AbstractAdapter adapter;

	protected Logger logger;

	private boolean loggedIn;
	protected DefaultMessageProcessor messageProcessor;

	/**
	 * Konstruktor anonymneho pouzivatela.
	 * Pouziva sa, ak este nie je jasne, o koho ide (pociatocne
	 * fazy prihlasovania).
	 */
	public AbstractUser(AbstractAdapter a, RemoteTalker t)
		throws RemoteException,
		InvalidPropertyValueException
	{
		logger = Logger.getLogger("nt.user");

		talker = t;
		adapter = a;
		properties = new HashMap();
		loggedIn = false;
		adapter.users.add(this);
		messageProcessor = new DefaultMessageProcessor(this);
		set(User.UPROP_LANG, getProperty(User.UPROP_LANG).defaultValue());
	}

	public Integer getId() {
		return (Integer) properties.get(User.UPROP_ID);
	}

	public Adapter getAdapter()
		throws RemoteException
	{
		return adapter;
	}

	public void set(String key, Object value)
		throws RemoteException,
		InvalidPropertyValueException
	{
		Property p = getProperty(key);
		if (propertiesConfiguration().getGlobal().contains(key) || propertiesConfiguration().getSettable().contains(key)) {
			value = p.toObject(value.toString());
		}
		if (p != null) {
			if (!p.checkValue(value)) {
				throw new InvalidPropertyValueException("Invalid value '" + value + "' for property " + key + ".");
			}
		}
		properties.put(key, value);
	}

	public Object get(String key) {
		Object rval = properties.get(key);
		if (rval == null) {
			return getProperty(key).defaultValue();
		}
		return rval;
	}

	public Map getProperties() {
		return properties;
	}

	public Property getProperty(String key) {
		return propertiesConfiguration().getProperty(key);
	}

	private PropertyConfiguration propertiesConfiguration() {
		return adapter.getUserPropertyConfiguration();
	}

	/**
	 * User's object part of work when disconnecting the adapter.
	 * <p>
	 * Adapter user object should do all clean up stuff in this method
	 * (eg. closing a socket). Only user's object state should be cleaned
	 * up. For disconnecting user from the talker is used adapter's
	 * userOut method. This method is also called from it.
	 * @see #disconnect(String)
	 *
	 * @throws RemoteException
	 */
	public abstract void disconnectCleanup() throws RemoteException;

	public void disconnect(String reason) throws RemoteException {
		if (reason != null) {
			getProperties().put(AdapterUser.UPROP_DISCONNECT_REASON, reason);
		}
		if (isLoggedIn()) {
			talker.userOut(this);
		} else {
			disconnectNotLoggedIn();
		}
		loggedIn = false;
	}

	private final void disconnectNotLoggedIn() throws RemoteException {
		adapter.disconnect(this);
	}

	public boolean isLoggedIn() {
		return loggedIn;
	}

	protected void registerLoggedIn() {
		logger = Logger.getLogger("nt.user." + getId());
		loggedIn = true;
		adapter.idUser.put(getId(), this);
	}

	protected void register(String tag, TagProcessor tagProcessor) {
		messageProcessor.registerTagProcessor(tag, tagProcessor);
	}

	protected String replace(String s, Replacer[] replacers) {
		for (int i = 0; i < replacers.length; i++) {
			s = replacers[i].use(s);
		}
		return s;
	}

	protected String getString(String key) {
		return (String) get(key);
	}

	protected class TagReplacer implements TagProcessor {
		private String to;

		public TagReplacer(String to) {
			this.to = to;
		}

		public String process(MessageProcessingContext ctx, String params) {
			return to;
		}
	}

	/**
	 * Replacer class.
	 */
	public static class Replacer {
		private Pattern p;
		private String to;

		private Replacer(Pattern p, String to) {
			this.p = p;
			this.to = to;
		}

		public static Replacer getReplacer(String from, String to) {
			return new Replacer(Pattern.compile(from), to);
		}

		public String use(String s) {
			Matcher m = p.matcher(s);
			return m.replaceAll(to);
		}
	}
}
